/**
 * Create in 2012-4-25.
 */
package kit4j.util.lang;

import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

/**
 * Date工具类。
 * 
 * @author MiniKnife
 * 
 */
public abstract class DateUtil {

	private static class SingletonHolder {

		static DateFormat defaultFormat = DateFormat.getInstance();

		static DateFormat defaultDateFormat = DateFormat.getDateInstance();

		static DateFormat defaultTimeFormat = DateFormat.getTimeInstance();

		static DateFormat defaultDateTimeFormat = DateFormat.getDateTimeInstance();

		static SimpleDateFormat defaultSimpleDateFormat = new SimpleDateFormat();

		static DateFormat getDefaultFormat() {
			return defaultFormat;
		}

		static DateFormat getDefaultDateFormat() {
			return defaultDateFormat;
		}

		static DateFormat getDefaultTimeFormat() {
			return defaultTimeFormat;
		}

		static DateFormat getDefaultDateTimeFormat() {
			return defaultDateTimeFormat;
		}

		static SimpleDateFormat getDefaultSimpleDateFormat() {
			return defaultSimpleDateFormat;
		}
	}

	/**
	 * 将{@link java.util.Date}对象转换为{@link java.sql.Date}对象。
	 * 
	 * @param date
	 *            要转换的{@link java.util.Date}对象
	 * @return 与指定{@link java.util.Date}对象表示同一时刻的{@link java.sql.Date}对象。
	 */
	public static java.sql.Date toSqlDate(Date date) {
		return new java.sql.Date(date.getTime());
	}

	/**
	 * 使用默认语言环境的Short风格将给定的Date格式化为日期/时间字符串。
	 * 
	 * @param date
	 *            要格式化的时间
	 * @return 已格式化的时间字符串
	 */
	public static String format(Date date) {
		return SingletonHolder.getDefaultFormat().format(date);
	}

	/**
	 * 使用默认语言环境的默认格式化风格将给定的Date格式化为日期字符串。
	 * 
	 * @param date
	 *            要格式化的日期
	 * @return 已格式化的日期字符串
	 */
	public static String formatDate(Date date) {
		return SingletonHolder.getDefaultDateFormat().format(date);
	}

	/**
	 * 使用默认语言环境的给定格式化风格将给定的Date格式化为日期字符串。
	 * 
	 * @param date
	 *            要格式化的日期
	 * @param style
	 *            给定的格式化风格
	 * @return 已格式化的日期字符串
	 */
	public static String formatDate(Date date, int style) {
		return DateFormat.getDateInstance(style).format(date);
	}

	/**
	 * 使用给定语言环境的给定格式化风格将给定的Date格式化为日期字符串。
	 * 
	 * @param date
	 *            要格式化的日期
	 * @param style
	 *            给定的格式化风格
	 * @param locale
	 *            给定的语言环境
	 * @return 已格式化的日期字符串
	 */
	public static String formatDate(Date date, int style, Locale locale) {
		return DateFormat.getDateInstance(style, locale).format(date);
	}

	/**
	 * 使用默认语言环境的默认格式化风格将给定的Date格式化为日期/时间字符串。
	 * 
	 * @param date
	 *            要格式化的时间
	 * @return 已格式化的时间字符串
	 */
	public static String formatTime(Date date) {
		return SingletonHolder.getDefaultTimeFormat().format(date);
	}

	/**
	 * 使用默认语言环境的给定格式化风格将给定的Date格式化为日期/时间字符串。
	 * 
	 * @param date
	 *            要格式化的时间
	 * @param style
	 *            给定的格式化风格
	 * @return 已格式化的时间字符串
	 */
	public static String formatTime(Date date, int style) {
		return DateFormat.getTimeInstance(style).format(date);
	}

	/**
	 * 使用给定语言环境的给定格式化风格将给定的Date格式化为日期/时间字符串。
	 * 
	 * @param date
	 *            要格式化的时间
	 * @param style
	 *            给定的格式化风格
	 * @param locale
	 *            给定的语言环境
	 * @return 已格式化的时间字符串
	 */
	public static String formatTime(Date date, int style, Locale locale) {
		return DateFormat.getTimeInstance(style, locale).format(date);
	}

	/**
	 * 使用默认语言环境的默认日期和时间格式化风格将给定的Date格式化为日期/时间字符串。
	 * 
	 * @param date
	 *            要格式化的时间
	 * @return 已格式化的时间字符串
	 */
	public static String formatDateTime(Date date) {
		return SingletonHolder.getDefaultDateTimeFormat().format(date);
	}

	/**
	 * 使用默认语言环境的给定日期和时间格式化风格将给定的Date格式化为日期/时间字符串。
	 * 
	 * @param date
	 *            要格式化的时间
	 * @param dateStyle
	 *            给定的日期格式化风格
	 * @param timeStyle
	 *            给定的时间格式化风格
	 * @return 已格式化的时间字符串
	 */
	public static String formatDateTime(Date date, int dateStyle, int timeStyle) {
		return DateFormat.getDateTimeInstance(dateStyle, timeStyle).format(date);
	}

	/**
	 * 使用给定语言环境的给定日期和时间格式化风格将给定的Date格式化为日期/时间字符串。
	 * 
	 * @param date
	 *            要格式化的时间
	 * @param dateStyle
	 *            给定的日期格式化风格
	 * @param timeStyle
	 *            给定的时间格式化风格
	 * @param locale
	 *            给定的语言环境
	 * @return 已格式化的时间字符串
	 */
	public static String formatDateTime(Date date, int dateStyle, int timeStyle, Locale locale) {
		return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale).format(date);
	}

	/**
	 * 用给定的模式和默认语言环境的日期格式符号将给定的Date格式化为日期/时间字符串。
	 * 
	 * @param date
	 *            要格式化的日期对象
	 * @param pattern
	 *            给定的格式化模式
	 * @return 已格式化的日期/时间字符串
	 */
	public static String formatDate(Date date, String pattern) {
		SimpleDateFormat dateFormat = SingletonHolder.getDefaultSimpleDateFormat();
		dateFormat.applyPattern(pattern);

		return dateFormat.format(date);
	}

	/**
	 * 用给定的模式和给定的语言环境的日期格式符号将给定的Date格式化为日期/时间字符串。
	 * 
	 * @param date
	 *            要格式化的日期对象
	 * @param pattern
	 *            给定的格式化模式
	 * @param locale
	 *            给定的语言环境
	 * @return 已格式化的日期/时间字符串
	 */
	public static String formatDate(Date date, String pattern, Locale locale) {
		SimpleDateFormat dateFormat = new SimpleDateFormat(pattern, locale);

		return dateFormat.format(date);
	}

	/**
	 * 用给定的模式和给定的日期格式符号将给定的Date格式化为日期/时间字符串。
	 * 
	 * @param date
	 *            要格式化的日期对象
	 * @param pattern
	 *            给定的格式化模式
	 * @param locale
	 *            给定的日期格式符号
	 * @return 已格式化的日期/时间字符串
	 */
	public static String formatDate(Date date, String pattern, DateFormatSymbols dateFormatSymbols) {
		SimpleDateFormat dateFormat = new SimpleDateFormat(pattern, dateFormatSymbols);

		return dateFormat.format(date);
	}

	/**
	 * 获取给定日期对象的给定日历字段的值。
	 * 
	 * @param date
	 *            日期对象
	 * @param field
	 *            字段指示值
	 * @return 给定日历字段的值。
	 * @throws NullPointerException
	 *             如果给定的日期对象为null。
	 */
	public static int get(Date date, int field) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		return cal.get(field);
	}

	/**
	 * 根据给定的字段值创建一个日期对象。字段值的按顺序依次指代年、月、日、时、分、秒、毫秒。
	 * 
	 * @param fields
	 *            字段值
	 * @return 根据给定的字段值创建的日期对象，如果给定的字段值全为空，则返回null。
	 */
	public static Date createDate(int... fields) {
		if (fields != null && fields.length > 0) {
			int[] fds = Arrays.copyOf(fields, 7);
			Calendar cal = Calendar.getInstance();

			cal.set(fds[0], fds[1], fds[2], fds[3], fds[4], fds[5]);
			cal.set(Calendar.MILLISECOND, fds[6]);

			return cal.getTime();
		} else {
			return null;
		}
	}

	/**
	 * 返回给定日期对象加上给定字段的值后的新日期对象。
	 * 
	 * @param date
	 *            日期对象
	 * @param field
	 *            要增加值的字段
	 * @param value
	 *            要增加的值
	 * @return 新日期对象。
	 * @throws NullPointerException
	 *             如果给定的日期对象为null。
	 */
	public static Date add(Date date, int field, int value) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(field, cal.get(field) + value);

		return cal.getTime();
	}

	/**
	 * 计算给定时间距离现在时间的天数。两个时间相距的天数是按现实时间的日期计算的，如当前时间为2010-11-30 00:00:00，被比较的时间是2010-11-29
	 * 23:59:59，则相距的天数为1天。
	 * 
	 * @param date
	 *            被比较的日期
	 * @return 给定时间距离现在时间的天数。
	 */
	public static int intervalDaysWithNow(Date date) {
		if (date != null) {
			return intervalDays(new Date(), date);
		} else {
			return 0;
		}
	}

	/**
	 * 根据系统默认时区设置计算给定两个时间相距的天数。两个时间相距的天数是按现实时间的日期计算的，如给定时间为2010-11-30 00:00:00，被比较的时间是2010-11-29
	 * 23:59:59，则相距的天数为1天。
	 * 
	 * @param origin
	 *            基准日期对象
	 * @param to
	 *            被比较的日期对象
	 * @return 两个时间相距的天数。
	 */
	public static int intervalDays(Date origin, Date to) {
		return intervalDays(origin, to, TimeZone.getDefault());
	}

	/**
	 * 根据给定时区设置计算给定两个时间相距的天数。两个时间相距的天数是按现实时间的日期计算的，如给定时间为2010-11-30 00:00:00，被比较的时间是2010-11-29
	 * 23:59:59，则相距的天数为1天。
	 * 
	 * @param origin
	 *            基准日期对象
	 * @param to
	 *            被比较的日期对象
	 * @param timeZone
	 *            时区
	 * @return 两个时间相距的天数。
	 */
	public static int intervalDays(Date origin, Date to, TimeZone timeZone) {
		if (origin != null || to != null) {
			final long DAY_MILLIS = TimeUnit.DAYS.toMillis(1);
			final long MILLI_OFFSET = timeZone.getRawOffset();
			long nowDays = (origin.getTime() + MILLI_OFFSET) / DAY_MILLIS;
			long toDays = (to.getTime() + MILLI_OFFSET) / DAY_MILLIS;

			return (int) (nowDays - toDays);
		} else {
			return 0;
		}
	}

	/**
	 * 获取给定的日期对象中所指示的日期最大的日期对象。
	 * 
	 * @param dates
	 *            要取最大值的日期对象数组
	 * @return 最大的日期对象。
	 * @throws NullPointerException
	 *             如果给定的日期对象中包含null值。
	 */
	public static Date max(Date... dates) {
		if (dates != null && dates.length > 0) {
			Date max = dates[0];

			for (int i = 1; i < dates.length; ++i) {
				if (dates[i].after(max)) {
					max = dates[i];
				}
			}

			return max;
		} else {
			return null;
		}
	}
}
